package com.qqt;

import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.List;
import java.util.stream.Collectors;

public class ProxyFactory<T,E> {

    private Class<T> daoClazz;
    private Class<E> entityClass;
    private JdbcPool jdbcPool = JdbcPool.getInstance();

    public ProxyFactory(Class<T> daoClass){
        this.daoClazz = daoClass;
        Type[] interTypes = daoClazz.getGenericInterfaces();
        ParameterizedType pType=(ParameterizedType) interTypes[0];
        Type[] types = pType.getActualTypeArguments();
        this.entityClass=(Class) types[0];
    }

    public  T getProxyInstance() {
        return (T) Proxy.newProxyInstance(
                daoClazz.getClassLoader(),
                new Class[]{daoClazz},
                (Object proxy, Method method, Object[] args)->{


                    if(method.getName().equals("get")){
                        Object arg = args[0];
                        Class clazz = entityClass;
                        List<Field> fields = ReflectUtil.fields(clazz);
                        StringBuilder stringBuilder = new StringBuilder("SELECT ");
                        stringBuilder.append(String.join(
                                ",",
                                fields.stream()
                                        .map(field -> field.getName())
                                        .collect(Collectors.toList())
                        ));
                        stringBuilder.append(" FROM "+clazz.getSimpleName().toUpperCase());
                        stringBuilder.append(" WHERE id = "+arg);
                        return  executeQuery(stringBuilder.toString(),clazz);
                    }else if(method.getName().equals("save")){
                        Object arg = args[0];
                        Class clazz = args[0].getClass();
                        List<Field> fields = ReflectUtil.fields(clazz);
                        StringBuilder stringBuilder = new StringBuilder("INSERT INTO "+ clazz.getSimpleName().toUpperCase()+"(");
                        stringBuilder.append(
                                String.join(
                                        ",",
                                        fields.stream()
                                                .map(field -> field.getName())
                                                .collect(Collectors.toList())
                                )
                        );
                        stringBuilder.append(") VALUES (");
                        stringBuilder.append(
                                String.join(
                                        ",",
                                        fields.stream()
                                                .map(field -> {
                                                    field.setAccessible(true);
                                                    try {
                                                        return getValue(field.get(arg));
                                                    } catch (IllegalAccessException e) {
                                                        e.printStackTrace();
                                                    }
                                                    return null;
                                                })
                                                .collect(Collectors.toList())
                                )
                        );
                        stringBuilder.append(")");
                        return executeUpdate(stringBuilder.toString());
                    }else if(method.getName().equals("update")){
                        Object arg = args[0];
                        Class clazz = args[0].getClass();
                        List<Field> fields = ReflectUtil.fields(clazz);
                        StringBuilder stringBuilder = new StringBuilder("UPDATE "+ clazz.getSimpleName().toUpperCase()+" SET ");
                        stringBuilder.append(
                                String.join(
                                        ",",
                                        fields.stream()
                                                .filter(field -> !field.getName().equals("id"))
                                                .map(field -> {
                                                    field.setAccessible(true);
                                                    try {
                                                        return  field.getName()+"="+getValue(field.get(arg));
                                                    } catch (IllegalAccessException e) {
                                                        e.printStackTrace();
                                                    }
                                                    return "";
                                                })
                                                .collect(Collectors.toList())
                                )
                        );
                        stringBuilder.append(" WHERE id = "+getId(arg));
                        return executeUpdate(stringBuilder.toString());
                    }else if(method.getName().equals("delete")){
                        Object arg = args[0];
                        Class clazz = entityClass;
                        StringBuilder stringBuilder = new StringBuilder("DELETE FROM "+clazz.getSimpleName().toUpperCase());
                        stringBuilder.append(" WHERE id  = "+arg);
                        return executeUpdate(stringBuilder.toString());
                    }else{
                        Object arg = args[0];
                        Sql sqlAnnotation = method.getAnnotation(Sql.class);
                        String sql = MessageFormat.format(sqlAnnotation.value(),arg);
                        return executeQuery(sql,(Class)method.getReturnType());
                    }
                }
        );
    }

    public Integer executeUpdate(String sql){
        System.out.println("SQL:"+sql);
        Connection conn = null;
        try{
            conn = jdbcPool.getConnection();
            Statement stmt = conn.createStatement();
            return stmt.executeUpdate(sql);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            jdbcPool.release(conn);
        }
        return -1;
    }

    public ResultSet executeQuery(String sql){
        System.out.println("SQL:"+sql);
        Connection conn = null;
        try{
            conn = jdbcPool.getConnection();
            Statement stmt = conn.createStatement();
            return stmt.executeQuery(sql);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            jdbcPool.release(conn);
        }
        return null;
    }
    public T executeQuery(String sql,Class<T> clazz){
        return ReflectUtil.resultSetTransEntity( executeQuery(sql),clazz);
    }

    public  Integer getId(Object obj){
        try {
            Field idField =  obj.getClass().getDeclaredField("id");
            idField.setAccessible(true);
            return (Integer)idField.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
    public  String getValue(Object obj){
        if(obj==null){
            return "null";
        }else if (obj instanceof String) {
            return "'"+obj.toString()+"'";
        }else{
            return obj.toString();
        }
    }
}
